#pragma once
#include <iostream>
#include <string>
#include <queue>
#include <pthread.h>
#include <unistd.h>

#include "../Cond/Cond.hpp"
#include "../Mutex/Mutex.hpp"

const int defaultcap = 5; // for test

using namespace MutexModule;
using namespace CondModule;

template<class T>
class BlockQueue {
private:
    bool IsFull() const { return _q.size() >= _cap; }
    bool IsEmpty() const { return !_q.size(); }
public:
    BlockQueue(int cap = defaultcap) : _cap(cap), _csleep_num(0), _psleep_num(0)
    {
        // 锁和条件变量已经被我们封装了，在初始化构造的时候就已经init了！
    }

    // 生产 --- 入队列
    void Equeue(const T& in) {
        // pthread_mutex_lock(&_mutex);
        LockGuard lockGuard(_mutex);
        while(IsFull()) {
            _psleep_num++;
            // pthread_cond_wait(&_full_cond, &_mutex);
            _full_cond.Wait(_mutex);
            _psleep_num--;
        }
        _q.push(in);
        if(_csleep_num > 0) {
            // pthread_cond_signal(&_empty_cond); // 不空了，别再被空导致休眠了
            _empty_cond.Signal();
            std::cout << "唤醒消费者..." << std::endl;
        }
        // pthread_mutex_unlock(&_mutex);
    }

    T Pop() {
        // pthread_mutex_lock(&_mutex);
        T data;
        {
            LockGuard lockGuard(_mutex);
            while(IsEmpty()) {
                _csleep_num++;
                // pthread_cond_wait(&_empty_cond, &_mutex);
                _empty_cond.Wait(_mutex);
                _csleep_num--;
            }
            data = _q.front();
            _q.pop();
            if(_psleep_num > 0) {
                // pthread_cond_signal(&_full_cond);// 不满了，别再被满导致休眠了
                _full_cond.Signal();
                std::cout << "唤醒生产者..." << std::endl;
            }
            // pthread_mutex_unlock(&_mutex);
            return data;
        }
    }

    ~BlockQueue() {
        // pthread_mutex_destroy(&_mutex);
        // pthread_cond_destroy(&_full_cond);
        // pthread_cond_destroy(&_empty_cond);
    }
private:
    std::queue<T> _q;
    int _cap;
    Mutex _mutex;
    Cond _full_cond;
    Cond _empty_cond;
    int _csleep_num;
    int _psleep_num;
};
